package com.spzx.product.service.impl;

import ch.qos.logback.core.joran.conditional.ElseAction;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.dialect.rowbounds.PostgreSqlRowBoundsDialect;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.product.api.domain.*;
import com.spzx.product.api.domain.Product;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import io.micrometer.core.instrument.internal.TimedExecutorService;
import lombok.extern.slf4j.Slf4j;
import nonapi.io.github.classgraph.utils.LogNode;
import org.apache.commons.math3.analysis.function.Log;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.convertor.TrueReplayConvertor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * ClassName: ProductImpl
 * Package: com.spzx.product.service.impl
 * Description:
 *
 * @Author ycy
 * @Create 2024/8/9 16:19
 * @Version 1.0
 */
@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;
    // 加入redis缓存
    @Autowired
    private RedisTemplate redisTemplate;

    //加入redissonClient
    @Autowired
    private RedissonClient redissonClient;


    /**
     * 查询商品列表
     *
     * @param product 商品
     * @return 商品
     */
    @Override
    public List<Product> selectProductList(Product product) {

        return productMapper.selectProductList(product);
    }

    /**
     * 批量删除商品
     *
     * @param ids 需要删除的商品主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteProductByIds(Long[] ids) {
        productMapper.deleteBatchIds(Arrays.asList(ids));
        //获取sku列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids).select(ProductSku::getId));
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
        productSkuMapper.delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId, ids));
        skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList));
        productDetailsMapper.delete(new LambdaQueryWrapper<ProductDetails>().in(ProductDetails::getProductId, ids));
        return 1;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        if(auditStatus == 1) {
            product.setAuditStatus(1);
            product.setAuditMessage("审批通过");
        } else {
            product.setAuditStatus(-1);
            product.setAuditMessage("审批不通过");
        }
        productMapper.updateById(product);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        if (status == 1){
            product.setStatus(1);
            // sku 加入布隆过滤器
            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");
            List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                    .eq(ProductSku::getProductId,id));
            productSkuList.forEach(item -> {
                // 将skuId 加入布隆过滤器
                bloomFilter.add(item.getId());
            });
        }else{
            product.setStatus(-1);
        }
        productMapper.updateById(product);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProduct(Product product) {
        baseMapper.updateById(product);
        List<ProductSku> productSkuList = product.getProductSkuList();
        productSkuList.forEach(productSku -> {
            //修改商品 SKU 信息
            productSkuMapper.updateById(productSku);
            //修改商品库存
            SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>()
                    .eq(SkuStock::getSkuId,productSku.getId()));
            skuStock.setTotalNum(productSku.getStockNum());
            int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();
            skuStock.setAvailableNum(availableNum);
            skuStockMapper.updateById(skuStock);
        });
        //修改商品详细信息
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId,product.getId()));
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.updateById(productDetails);

        return 1;

    }

    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .in(ProductSku::getId,skuIdList)
                .select(ProductSku::getId,ProductSku::getSalePrice));
        return productSkuList.stream().map(item -> {
            SkuPrice skuPrice = new SkuPrice();
            skuPrice.setSkuId(item.getId());
            skuPrice.setSalePrice(item.getSalePrice());
            return skuPrice;
        }).collect(Collectors.toList());
    }



    /**
     * 新增商品
     * @param product
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertProduct(Product product) {
        productMapper.insert(product);

        List<ProductSku> productSkuList = product.getProductSkuList();
        for (int i = 0, size = productSkuList.size(); i < size; i++) {
            ProductSku productSku = productSkuList.get(i);
            productSku.setSkuCode(product.getId() + "_" + i);
            productSku.setProductId(product.getId());
            String skuName = product.getName() + " " + productSku.getSkuSpec();
            productSku.setSkuName(skuName);
            productSkuMapper.insert(productSku);

            //添加商品库存
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            skuStock.setTotalNum(productSku.getStockNum());
            skuStock.setLockNum(0);
            skuStock.setAvailableNum(productSku.getStockNum());
            skuStock.setSaleNum(0);
            skuStockMapper.insert(skuStock);
        }

        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(product.getId());
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.insert(productDetails);

        return product.getId().intValue();
    }

    @Override
    public Product selectProductById(Long id) {
        //商品信息
        Product product = productMapper.selectById(id);

        //商品SKU列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
        //查询库存
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
        List<SkuStock> skuStockList = skuStockMapper.selectList(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList).select(SkuStock::getSkuId, SkuStock::getTotalNum));
        Map<Long, Integer> skuIdToStockNumMap = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));
        productSkuList.forEach(item -> {
            item.setStockNum(skuIdToStockNumMap.get(item.getId()));
        });
        product.setProductSkuList(productSkuList);

        //商品详情
        ProductDetails productDetails = productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
        product.setDetailsImageUrlList(Arrays.asList(productDetails.getImageUrls().split(",")));
        return product;
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {

        String key = "sku:checkAndLock:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo;

        // 防止重复请求
        boolean isExist = redisTemplate.opsForValue().setIfAbsent(key,orderNo,1,TimeUnit.HOURS);
        if (!isExist) {
            // 如果存在，则已执行过库存锁定
            if (redisTemplate.hasKey(dataKey)){
                return "";

            }else {
                return "重复提交";
            }
        }

        // 遍历所有商品，验证库存并锁定，要求具备原子性
        skuLockVoList.forEach(skuLockVo -> {
            // 验证库存：查询返回的是满足要求的库存列表
            SkuStock skuStock = skuStockMapper.check(skuLockVo.getSkuId(),skuLockVo.getSkuNum());

            // 如果没有一个商品满足要求这里就验证失败
            if (null == skuStock){
                skuLockVo.setIsHaveStock(false);
            }else {
                skuLockVo.setIsHaveStock(true);
            }
        });

        // 只要有一个商品锁定失败，所有锁定成功的商品都要解锁库存
        if (skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())){

            // 获取所有锁定成功的商品，遍历解锁库存
            StringBuffer result = new StringBuffer();

            // 获取没有库存的对象列表
            List<SkuLockVo> noHaveStockSkuLockVoList = skuLockVoList.stream().filter(item -> !item.getIsHaveStock()).collect(Collectors.toList());
            for (SkuLockVo skuLockVo : noHaveStockSkuLockVoList) {
                // 解除去重
                this.redisTemplate.delete(key);
                result.append("商品:" + skuLockVo.getSkuId() + "库存不足;");
            }

            // 锁定失败，解除
            redisTemplate.delete(key);

            // 响应锁定状态
            return result.toString();
        }else {

            // 锁定库存
            skuLockVoList.forEach(skuLockVo -> {
                int row = skuStockMapper.lock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
                if (row == 0){
                    //解除去重
                    this.redisTemplate.delete(key);
                    throw new ServiceException("库存锁定失败");
                }
            });
        }

        // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到 redis ，以方便将来解锁库存 或者 减少库存
        this.redisTemplate.opsForValue().set(dataKey,skuLockVoList);

        return "";
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void unlock(String orderNo) {
        String key = "sku:unlock:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo;

        // 业务去重防止重复消费
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key,orderNo,1, TimeUnit.HOURS);
        if (!isExist) return;

        // 获取锁定库存的缓存信息
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) this.redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)){
            return;
        }

        // 解锁库存
        skuLockVoList.forEach(skuLockVo -> {
            int row = skuStockMapper.unlock(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if (row == 0) {
                // 解除去重
                this.redisTemplate.delete(key);
                throw new ServiceException("解锁出库失败");
            }
        });

        // 解锁库存之后，删除锁定库存的缓存。以防止重复解锁库存
        this.redisTemplate.delete(dataKey);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void minus(String orderNo) {
        String key  = "sku:minus:" + orderNo;
        String dataKey = "sku:lock:data:" + orderNo;
        // 业务去重，防止重复消费
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key,orderNo,1,TimeUnit.HOURS);
        if (!isExist) return;

        // 获取锁定库存的缓存信息
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) this.redisTemplate.opsForValue().get(dataKey);

        if (CollectionUtils.isEmpty(skuLockVoList)){
            return ;
        }

        // 减库存
        skuLockVoList.forEach(skuLockVo -> {
            int row = skuStockMapper.minus(skuLockVo.getSkuId(),skuLockVo.getSkuNum());
            if (row == 0){
                //解除去重
                this.redisTemplate.delete(key);
                throw new ServiceException("减出库失败");
            }
        });

        // 解锁库存之后，删除锁定库存的缓存。以防止重复删除
        this.redisTemplate.delete(dataKey);

    }


    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.selectTopSale();
    }

    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectProductSkuList(skuQuery);
    }

    /**
     * 根据 SkuID查询SKU商品信息
     *
     * @param skuId
     * @return
     */
    @Override
    public ProductSku getProductSku(Long skuId) {
        try {
            // 1.优先从缓存中获取数据
            // 1.1 构建业务数据Key 形式：前缀 + 业务唯一标识
            String dataKey = "product:sku" + skuId;
            // 1.2 查询Redis获取业务数据
            ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
            // 1.3 命中缓存则直接返回
            if (productSku != null) {
                log.info("命中缓存，直接返回，线程ID:{}，线程名称:{}", Thread.currentThread().getName());
                return productSku;
            }

            // 进入到这一步表示对应 skuId 的 productSku 不存在，下面执行获取分布式锁的操作

            // 2.尝试获取分布式锁（set k v ex nx 可能获取锁失败）
            // 2.1 构建锁key
            String lockKey = "product:sku:lock:" + skuId;
            // 2.2 采用UUID作为线程标识
            String lockVal = UUID.randomUUID().toString().replaceAll("-", "");
            // 2.3 利用Redis提供set nx ex 获取分布式锁
                                                        //setIfAbsent用法：如果键存在则不执行插入操作(false)，返回与键关联的当前值
                                                        //如果键不存在，则执行插入操作(true)，返回null值
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
            if (flag) {
                // 3. 获取锁成功执行业务，将查询业务数据放入缓存Redis
                log.info("获取锁成功：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                try {
                    productSku = this.getProductSkuFromDB(skuId);
                    long ttl = productSku == null ? 1 * 60 : 10 * 60;
                    redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);
                    return productSku;
                } finally {
                    // 4.业务执行完毕释放锁
                    String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" + "then\n" + "return redis.call(\"del\",KEYS[1])\n" + "else\n" + "return 0\n" + "end";
                    // 此对象用于存储Lua脚本的内容和返回结果的类型
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    redisScript.setScriptText(scriptText);
                    redisScript.setResultType(Long.class);
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);

                }
            } else {
                try {
                    //5. 获取锁失败则自旋（业务要求必须执行）
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
                return this.getProductSku(skuId);
            }
        } catch (Exception e){
            //兜底处理方案：Redis服务由问题，自动从数据库获取业务数据
            log.error("[商品服务]查询商品信息异常：{}", e);
            return this.getProductSkuFromDB(skuId);
        }
    }

    private ProductSku getProductSkuFromDB(Long skuId) {
        // mysql
        ProductSku productSku = productSkuMapper.selectById(skuId);
        return productSku;
    }
    @GuiguCache(prefix = "product:")
    @Override
    public Product getProduct(Long id) {
        return productMapper.selectById(id);
    }

    @Override
    public SkuPrice getSkuPrice(Long skuId) {
        ProductSku productSku = productSkuMapper.selectOne(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getId, skuId)
                .select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        SkuPrice skuPrice = new SkuPrice();
        BeanUtils.copyProperties(productSku, skuPrice);

        return skuPrice;
    }
    @GuiguCache(prefix = "productDetails:")
    @Override
    public ProductDetails getProductDetails(Long id) {
        return productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId, id));
    }
    @GuiguCache(prefix = "skuSpecValue:")
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
                .eq(ProductSku::getProductId, id)
                .select(ProductSku::getId, ProductSku::getSkuSpec));
        Map<String, Long> skuSpecValueMap = new HashMap<>();
        productSkuList.forEach(item -> {
            skuSpecValueMap.put(item.getSkuSpec(), item.getId());
        });
        return skuSpecValueMap;
    }

    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>()
                .eq(SkuStock::getSkuId, skuId));
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }


}
